package com.hiq.gene.engine;

import com.hiq.gene.exception.InvalidEntityException;
import com.hiq.gene.exception.InvalidMethodException;
import com.hiq.gene.exception.InvalidTypeException;
import com.hiq.gene.model.DAO;
import com.hiq.gene.model.Entity;
import com.hiq.gene.model.Types;

import javax.xml.bind.JAXBException;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

public class CodeGenerator {
    public static final String DEFAULT_PROJECT_FILE = "project.hg";

    private List<Entity> entities = new ArrayList<Entity>();
    private List<DAO> daos = new ArrayList<DAO>();

    private Configuration config;
    private List<String> entityTypes = new ArrayList<String>();

    public CodeGenerator() {
        config = Configuration.getInstance();
    }

    public CodeGenerator(String configPath) {
        config = Configuration.getInstance(configPath);
    }

    public void generate() throws JAXBException {
        String fileContest = readFile();
        String[] lexs = fileContest.split(";");
        for (String s : lexs) {
            parseLexem(s);
        }

        prepareEntityTypes(entities);
        checkEntities(entities);

        PostgresqlGenerator dbGenerator = new PostgresqlGenerator(config.getProperty("db.dir"));
        try {
            dbGenerator.generate(entities);
        } catch (InvalidTypeException e) {
            System.out.println(e.getMessage());
            System.exit(0);
        }

        JpaGenerator jpaGen = new JpaGenerator(config.getProperty("src.dir"), config.getProperty("package"));
        try {
            jpaGen.generate(entities);
        } catch (InvalidTypeException e) {
            System.out.println(e.getMessage());
            System.exit(0);
        }
    }

    private void checkEntities(List<Entity> entities) {
        for (Entity e : entities) {
            Collection<String> types = e.getFields().values();
            for (String type : types) {
                if (type.startsWith("@")) {
                    if (!entityTypes.contains(type.substring(1))) {
                        System.out.println("Invalid entity type [" + type + "] in entity " + e.getName());
                        System.exit(0);
                    }
                } else {
                    if (!Types.typeExists(type)) {
                        System.out.println("No such type [" + type + "] in entity " + e.getName());
                        System.exit(0);
                    }
                }
            }
        }
    }

    private void prepareEntityTypes(List<Entity> entities) {
        entityTypes = new ArrayList<String>();
        for (Entity e : entities) {
            entityTypes.add(e.getName());
        }
    }

    private void parseLexem(String s) {
        if (s.startsWith("@")) {
            try {
                Entity entity = Parser.parseEntity(s.substring(1));
                entities.add(entity);
            } catch (InvalidEntityException e) {
                System.out.println("Can't parse entity: " + s);
                System.out.println(e.getMessage());
                System.exit(0);
            }
        } else if (s.startsWith("$")) {
            try {
                DAO dao = Parser.parseDAO(s.substring(1));
            } catch (InvalidMethodException e) {
                System.out.println("Can't parse dao: " + s);
                System.out.println(e.getMessage());
                System.exit(0);
            }
        }
    }

    private String readFile() {
        String contest = "";
        try {
            BufferedReader br = new BufferedReader(new FileReader(DEFAULT_PROJECT_FILE));
            String str;
            while ((str = br.readLine()) != null) {
                if (!str.startsWith("#")) {
                    contest = contest + str.trim();
                }
            }

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return contest;
    }
}
